//
//
// *  $Id: TestStress.java,v 1.5 2003/09/21 15:49:02 boisvert Exp $
//
// *
//
// *  Package stress test
//
// *
//
// *  Simple db toolkit
//
// *  Copyright (C) 1999, 2000 Cees de Groot <cg@cdegroot.com>
//
// *
//
// *  This library is free software; you can redistribute it and/or
//
// *  modify it under the terms of the GNU Library General Public License
//
// *  as published by the Free Software Foundation; either version 2
//
// *  of the License, or (at your option) any later version.
//
// *
//
// *  This library is distributed in the hope that it will be useful,
//
// *  but WITHOUT ANY WARRANTY; without even the implied warranty of
//
// *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//
// *  Library General Public License for more details.
//
// *
//
// *  You should have received a copy of the GNU Library General Public License
//
// *  along with this library; if not, write to the Free Software Foundation,
//
// *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
//
// 

//*************************************************************************
//**  Included in JDMB 1.0 port to SharpDBM;  11/2013 Cyrus Neah cneah@codingreal.com
//**  SharpDBM is an independent reimplementation of the JDBM 1.0 software library  
//*************************************************************************

using System;
using SharpDBM;
using SharpDBM.recman;
using SharpDBM.helper;
using SharpDBM.btree;
using NUnit.Framework;



namespace SharpDBM.Test

.recman
{



	using RecordManager = SharpDBM.RecordManager;

	using RecordManagerFactory = SharpDBM.RecordManagerFactory;



	using ByteArraySerializer = SharpDBM.helper.ByteArraySerializer;



	using NUnit.Framework;




///
//
// *  This class contains stress tests for this package.
//
// 

	[TestFixture]
	public class TestStress  
	{



		



		[Test]
 public virtual void setUp()
		{

			TestRecordFile.deleteTestFile();

		}



		[Test]
 public virtual void tearDown()
		{

			TestRecordFile.deleteTestFile();

		}



	// test parameters

		internal readonly int RECORDS = 100000;
		//internal readonly int RECORDS = 100;

		internal readonly int MAXSIZE = 500;

		internal readonly int ROUNDS = 1 * 10000 ;
	    //internal readonly int ROUNDS = 100;



		internal readonly int RPPROMILLE = 1 * 1000 * 10 / 1000;
	    //internal readonly int RPPROMILLE = 1 * 10 * 10 / 10;



		internal Random rnd = new Random(42);





	// holder for record data so we can compare

		internal class RecordData
		{

			internal long rowid;

			internal int size;

			internal sbyte b;



			internal RecordData(long rowid, int size, sbyte b)
			{

				this.rowid = rowid;

				this.size = size;

				this.b = b;

			}



			public override string ToString()
			{

				return "slot(" + rowid + ",sz=" + size + ",b=" + b + ")";

			}

		}



		private int getRandomAllocatedSlot(RecordData[] d)
		{
			int slot = 0;
			while (d[slot] != null)
			{
				 slot++;
				 if (slot == RECORDS) break;
			}

			while (d[slot] == null && slot !=0)
			{

				slot++;

				if (slot == RECORDS)

					slot = 0; // wrap

			}

			return slot;

		}



	// holder for root records

		internal long[] roots = new long[FileHeader.NROOTS];



		private int getRandomAllocatedRoot()
		{

			int slot = rnd.Next(FileHeader.NROOTS);

			while (roots[slot] == 0)
			{

				slot++;

				if (slot == FileHeader.NROOTS)

					slot = 0; // wrap

			}

			return slot;

		}



//    *
//
//     *  Test basics
//
//     

 
//
		[Test]
 public virtual void testBasics()
		{

			RecordManager recman;

			//System.Configuration.ConfigurationManager.AppSettings["DISABLE_TRANSACTIONS"] = "true";

			recman = RecordManagerFactory.createRecordManager(TestRecordFile.testFileName);


			
		// as this code is meant to test data structure calculcations

		// and stuff like that, we may want to disable transactions

		// that just slow us down.

		  //mgr.disableTransactions();



			RecordData[] d = new RecordData[RECORDS];

			int recordCount = 0, rootCount = 0;

			int inserts = 0, updates = 0, deletes = 0, fetches = 0;

			int rootgets = 0, rootsets = 0;

			int slot = -1;



			try
			{



                for (int i = 0; i < ROUNDS ; i++)
                {

                    if ((i % RPPROMILLE) == 0)

                        Console.Write("\rComplete: " + i / RPPROMILLE + "/1000th");



                    // close and re-open a couple of times during the

                    // test, in order to check flushing etcetera.

                    if ((i % (ROUNDS / 5)) == 0)
                    {

                        Console.Write(" (reopened at round " + i / RPPROMILLE + ")");
                        d = new RecordData[RECORDS];
                        recman.commit();
                        recman.close();
                        TestRecordFile.deleteFile(TestRecordFile.testFileName);
                        TestRecordFile.deleteFile(TestRecordFile.testFileName + RecordFile.extension);
                        TestRecordFile.deleteFile(TestRecordFile.testFileName + TransactionManager.extension);

                        recman = RecordManagerFactory.createRecordManager(TestRecordFile.testFileName);

                        //        recman.disableTransactions();

                    }



                    // generate a random number and assign ranges to operations:

                    // 0-10 = insert, 20 = delete, 30-50 = update, 51 = set root,

                    // 52 = get root, rest = fetch.

                    int op = rnd.Next(100);

                    if (op <= 10)
                    {

                        // INSERT RECORD

                        if (recordCount == RECORDS)
                        {

                            i -= 1;

                            continue;

                        }



                        slot = 0;

                        while (d[slot] != null)

                            slot++;



                        d[slot] = new RecordData(0, rnd.Next(MAXSIZE), (sbyte)rnd.Next());

                        d[slot].rowid = recman.insert(TestUtil.makeRecord(d[slot].size, d[slot].b));

                        recordCount++;

                        inserts++;

                    }

                    else if (op == 20)
                    {

                        // DELETE RECORD

                        if (recordCount == 0)
                        {

                            i -= 1;

                            continue;

                        }


                        if (d[0] != null)
                        {
                            slot = getRandomAllocatedSlot(d);

                            recman.delete(d[slot].rowid);

                            d[slot] = null;

                            recordCount--;

                            deletes++;
                        }

                    }

                    else if (op <= 50)
                    {

                        // UPDATE RECORD

                        if (recordCount == 0)
                        {

                            i -= 1;

                            continue;

                        }


                        if (d[0] != null)
                        {
                            slot = getRandomAllocatedSlot(d);

                            d[slot].size = rnd.Next(MAXSIZE);

                            d[slot].b = (sbyte)rnd.Next();

                            recman.update(d[slot].rowid, TestUtil.makeRecord(d[slot].size, d[slot].b));

                            updates++;
                        }

                    }

                    else if (op == 51)
                    {

                        // SET ROOT

                        int root = rnd.Next(FileHeader.NROOTS);

                        roots[root] = (long)rnd.NextDouble();

                        recman.setRoot(root, roots[root]);

                        rootsets++;

                    }

                    else if (op == 52)
                    {

                        // GET ROOT

                        if (rootCount == 0)
                        {

                            i -= 1;

                            continue;

                        }



                        int root = getRandomAllocatedRoot();

                        NUnit.Framework.Assert.AreEqual(roots[root], recman.getRoot(root), "root");

                        rootgets++;

                    }

                    else
                    {

                        // FETCH RECORD

                        if (recordCount == 0)
                        {

                            i -= 1;

                            continue;

                        }

                        if (d[0] != null)
                        {

                            slot = getRandomAllocatedSlot(d);

                            sbyte[] data = (sbyte[])recman.fetch(d[slot].rowid, ByteArraySerializer.INSTANCE);

                            NUnit.Framework.Assert.IsTrue(TestUtil.checkRecord(data, d[slot].size, d[slot].b), "fetch round=" + i + ", slot=" + slot + ", " + d[slot]);

                            fetches++;
                        }

                    }

                }

				recman.close();

						}
			catch (System.Exception e)
			{

				Console.WriteLine(e.StackTrace);

				NUnit.Framework.Assert.Fail("aborting test at slot " + slot + ": " + e);

			}
			finally
			{

				Console.WriteLine("records : " + recordCount);

				Console.WriteLine("deletes : " + deletes);

				Console.WriteLine("inserts : " + inserts);

				Console.WriteLine("updates : " + updates);

				Console.WriteLine("fetches : " + fetches);

				Console.WriteLine("rootget : " + rootgets);

				Console.WriteLine("rootset : " + rootsets);

				int totalSize = 0;

				for (int i = 0; i < RECORDS; i++)

				if (d[i] != null)

					totalSize += d[i].size;

				Console.WriteLine("total outstanding size: " + totalSize);



			//System.out.println("---");

			//for (int i = 0; i < RECORDS; i++)

			//    if (d[i] != null)

			//        System.out.println("slot " + i + ": " + d[i]);

			}



		}



//    *
//
//     *  Runs all tests in this class
//
//     

		static void Main(string[] args)
		{

			//// junit.textui.TestRunner.run(new TestSuite(typeof(TestStress)));

		}

	}


}